//
// Created by yunnan xu on 2020/12/7.
//

#include "MeshModel.h"
#include "Model.h"

#include <utility>
#include "MeshHelper.h"
#include "Singleton.h"
#include "ResourceMgr.h"
#include "assimp/scene.h"
#include "assimp/mesh.h"
#include "assimp/postprocess.h"

MeshModel::MeshModel(const char * model_path, std::unordered_set<VertexAttribType> _attrib_list)
{
    long file_size = ResourceMgr::GetInstance()->GetFileLength(model_path, AASSET_MODE_BUFFER);
    modelFileContent = new unsigned char [file_size];
    attrib_list = std::move(_attrib_list);

    int read_res = ResourceMgr::GetInstance()->ReadFile(model_path, modelFileContent, file_size, AASSET_MODE_BUFFER);
    if (read_res < 0)
    {
        LOGE("Texture2D ReadFile error filepath = %s, res = %d", model_path, read_res);
        return;
    }
    else {
        LOGI("Texture2D filepath = %s, read_res = %d", model_path, read_res);
    }

    ai_scene = importer.ReadFileFromMemory(modelFileContent, file_size, aiPostProcessSteps::aiProcess_Triangulate | aiPostProcessSteps::aiProcess_FlipUVs, "");

    meshes = ai_scene->mMeshes;
    meshNumber = ai_scene->mNumMeshes;

    assert(meshNumber > 0);

    aiMesh* first_mesh = meshes[0];

    vertexNumber = first_mesh->mNumVertices;

    // 初始化顶点属性缓冲区大小
    for (auto attrib_type : attrib_list) {
        InitVertexAttrib(attrib_type);
    }

    int index = 0;
    float * position_data = (float *) vertex_data[VertexAttribType::POSITION_F3];
    float * normal_data = (float *) vertex_data[VertexAttribType::NORMAL_F3];
    float * uv_data = (float *) vertex_data[VertexAttribType::UV1_F2];

    for (int i = 0; i < vertexNumber; i ++)
    {
        position_data[index * 3 + 0] = first_mesh->mVertices[i].x;
        position_data[index * 3 + 1] = first_mesh->mVertices[i].y;
        position_data[index * 3 + 2] = first_mesh->mVertices[i].z;
        index ++;
    }

    faceNumber = 0;
    for (int i = 0; i < first_mesh->mNumFaces; i ++)
    {
        unsigned int indices_number = first_mesh->mFaces[i].mNumIndices;
        for (int j = 1; j <= indices_number - 2; j ++)
        {
            index_data.push_back(first_mesh->mFaces[i].mIndices[0]);
            index_data.push_back(first_mesh->mFaces[i].mIndices[j]);
            index_data.push_back(first_mesh->mFaces[i].mIndices[j + 1]);
            faceNumber ++;
        }
    }

    for (int i = 0; i < vertexNumber; i++)
    {
        uv_data[i * 2 + 0] = first_mesh->mTextureCoords[0][i].x;
        uv_data[i * 2 + 1] = first_mesh->mTextureCoords[0][i].y;
    }

    for (int i = 0; i < vertexNumber; i++)
    {
        normal_data[i * 3 + 0] = first_mesh->mNormals[i].x;
        normal_data[i * 3 + 1] = first_mesh->mNormals[i].y;
        normal_data[i * 3 + 2] = first_mesh->mNormals[i].z;
    }
}

void MeshModel::InitVertexAttrib(VertexAttribType va_type)
{
    assert(vertexNumber > 0);

    if (vertex_data.find(va_type) != vertex_data.end())
    {
        LOGI("Vertex Attribute already exists!");
        return;
    }

    size_t vertex_attrib_buffer_size = vertexNumber *
                                       GLESVertexAttribToSize[(int) va_type] *
                                       GLESVertexAttribToCount[(int) va_type];
    vertex_data.emplace(va_type, new uint8_t[vertex_attrib_buffer_size]);
}

int MeshModel::GetVertexNumber() const
{
    return vertexNumber;
}

int MeshModel::GetFaceNumber() const
{
    return faceNumber;
}

const void * MeshModel::GetVertexData(VertexAttribType attrib_type) const
{
    auto vertex_data_pair = vertex_data.find(attrib_type);
    if (vertex_data_pair == vertex_data.end())
    {
        return nullptr;
    }
    return (const void *) vertex_data_pair->second;
}

size_t MeshModel::GetVertexDataLength(VertexAttribType attrib_type) const
{
    return vertexNumber * GLESVertexAttribToSize[(int)attrib_type] * GLESVertexAttribToCount[(int)attrib_type];
}

const void * MeshModel::GetIndexData() const
{
    return (const void *) index_data.data();
}

size_t MeshModel::GetIndexDataLength() const
{
    return sizeof(uint32_t) * faceNumber * 3;
}

GLenum MeshModel::GetIndexType() const
{
    return GL_UNSIGNED_INT;
}

MeshModel::~MeshModel() {
    // delete sceneData;
}